package com.db.stexchem.server;

import java.io.IOException;
import java.net.Socket;
import java.util.List;

import com.db.stexchem.exceptions.BadAuthenticationException;
import com.db.stexchem.exceptions.BadRequestException;
import com.db.stexchem.message.ExecutionReport;
import com.db.stexchem.message.LoginRequest;
import com.db.stexchem.message.LoginResponse;
import com.db.stexchem.message.Order;
import com.db.stexchem.message.PlaceOrderResponse;
import com.db.stexchem.message.Status;
import com.db.stexchem.serialization.Serializer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ClientSession implements Runnable {
  
  private final Logger              logger = LoggerFactory
                                               .getLogger(ClientSession.class);
  private final Socket              socket;
  private final Serializer          serializer;
  private final LoginService        loginService;
  private final OrderService        orderService;
  private final NotificationService notificationService;
  
  ClientSession(final Socket socket, final LoginService loginService,
      final OrderService orderService, final NotificationService notificationService,
      final Serializer serializer) throws IOException {
    this.serializer = serializer;
    this.socket = socket;
    this.loginService = loginService;
    this.orderService = orderService;
    this.notificationService = notificationService;
  }
  
  public void run() {
    logger.trace("started new session");
    String login = null;
    try {
      login = login();
      while (true) {
        placeOrder(login);
      }
    } catch (IOException e) {
      logger.error(e.getMessage());
      loginService.logout(login);
      try {
        socket.close();
      } catch (IOException exc) { // don't care
      }
    }
  }
  
  private String login() throws IOException {
    String login;
    LoginRequest request = serializer.deserializeLoginRequest(socket
        .getInputStream());
    try {
      login = loginService.login(request, this);
    } catch (BadAuthenticationException e) {
      logger.error("failed to authentificate client: {}", e.getMessage());
      LoginResponse response = new LoginResponse();
      response.setStatus(Status.BAD_REQUEST);
      response.setErrorMessage(e.getMessage());
      serializer.serialize(socket.getOutputStream(), response);
      throw new IOException("bad authentification", e);
    }
    LoginResponse loginResponse = new LoginResponse();
    loginResponse.setStatus(Status.OK);
    serializer.serialize(socket.getOutputStream(), loginResponse);
    notificationService.sendWaitingNotifications(login);
    return login;
  }
  
  private void placeOrder(final String login) throws IOException {
    Order order = serializer.deserializeOrder(socket.getInputStream());
    logger.info("received {} from client {}", order, login);
    try {
      orderService.checkOrder(order);
    } catch (BadRequestException e) {
      logger.error("failed to place order: {}", e.getMessage());
      PlaceOrderResponse response = new PlaceOrderResponse();
      response.setStatus(Status.BAD_REQUEST);
      response.setErrorMessage(e.getMessage());
      serializer.serialize(socket.getOutputStream(), response);
      return;
    }
    order.setOrderNo(orderService.getNextOrderNo());
    order.setParty(login);
    PlaceOrderResponse response = new PlaceOrderResponse();
    response.setStatus(Status.OK);
    response.setOrderNo(order.getOrderNo());
    List<Match> matches = orderService.placeOrder(order);
    logger.info("{} has been success fully placed to order book", order);
    serializer.serialize(socket.getOutputStream(), response);
    notificationService.sendExecutionReports(matches);
  }
  
  public void sendNotification(final ExecutionReport report) throws IOException {
    serializer.serialize(socket.getOutputStream(), report);
  }
}
